#!/usr/bin/python
# -*- coding: utf-8 -*-

"""
Get statistics about VMs in cloud
"""
import subprocess
import string
import re
import logging
import sys
import time
import datetime
import MySQLdb
import ConfigParser
#import paramiko
#import socket

logging.basicConfig(filename='/var/log/ost-statistics/statistics.log', level=logging.INFO, format='%(asctime)s %(levelname)s: %(message)s')

def GetStatistics():
  """ Get statistics about VMs at host.
      Returns map whith statistics about VMs.
  """
  logging.info('/ Start GetStatistics \\')
  try:
    # Get host name
    p = subprocess.Popen('hostname', stdout = subprocess.PIPE, stderr = subprocess.PIPE)
    out, err = p.communicate()
    if(len(string.strip(err)) > 0):
      logging.warning(err)
    host_name = string.strip(out)
    logging.info('host_name = %s', host_name)

    # List of openstack guests VMs on this host
    VM_name_list = []
    command_get_VM_list = string.split('nova-manage vm list')
    p = subprocess.Popen(command_get_VM_list, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
    communicate = p.communicate()
    if(len(string.strip(communicate[1])) > 0):
      logging.warning(communicate[1])
    VM_list = string.split(communicate[0], '\n')
    for line in VM_list:
      if re.search('active', line) and re.search(host_name, line):
        split_line = string.split(line)
        VM_name = split_line[0]
        VM_name_list.append(VM_name)
  
    # Fill map (libvirt) -> (VM parameters)
    VM_map = {}
  
    # Get VM libvirt name
    for name in VM_name_list:
      command_get_VM_info = string.split('nova show ' + name)
      p = subprocess.Popen(command_get_VM_info, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
      communicate = p.communicate()
      if(len(string.strip(communicate[1])) > 0):
        logging.warning(communicate[1])
      info_list = string.split(communicate[0], '\n')
      for line in info_list:
        if re.search('instance_name', line):
          split_line = string.split(line)
          instance = split_line[3]
          VM_map[instance] = {'VM_name': name, 'host':host_name}
          break
  
    # Number of processors is needed to correct pcpufrom 'top'
    command_get_proc_number = string.split('grep processor /proc/cpuinfo')
    p = subprocess.Popen(command_get_proc_number, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
    communicate = p.communicate()
    if(len(string.strip(communicate[1])) > 0):
      logging.warning(communicate[1])
    proc_string = string.strip(communicate[0])
    proc_list = string.split(proc_string, '\n')
    proc_num = len(proc_list)
    logging.info('proc_num = %d'%proc_num)
  
    # Get VM pid
    command_get_ps = string.split('ps -o pid=,args= -C qemu-system-x86_64')
    p = subprocess.Popen(command_get_ps, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
    communicate = p.communicate()
    if(len(string.strip(communicate[1])) > 0):
      logging.warning(communicate[1])
    pid_list = string.split(communicate[0], '\n')
    for line in pid_list:
      parse = re.search('\s*(?P<pid>\d*)\s*.*\s*-name\s*(?P<instance>[a-zA-Z0-9_-]*)\s*.*', line)
      if parse:
        pid = parse.group('pid')
        instance = parse.group('instance')
        if instance in VM_map:
          VM_map[instance]['pid'] = pid
  
    # Get VM CPU
    for instance in VM_map:
      pid = VM_map[instance]['pid']
      command_get_top = string.split('top -b -n 1 -p ' + pid)
      p = subprocess.Popen(command_get_top, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
      communicate = p.communicate()
      if(len(string.strip(communicate[1])) > 0):
        logging.warning(communicate[1])
      top_list = string.split(communicate[0], '\n')
      for line in top_list:
        if re.search(pid, line):
          split_line = string.split(line)
          CPU = split_line[8]
          VM_map[instance]['CPU'] = float(CPU) / float(proc_num)
          VM_map[instance]['stat_date'] = datetime.datetime.now().isoformat(' ')
  
    logging.info(VM_map)
    return VM_map
  except Exception as e:
     logging.warning(e)
     raise e
#    logging.warning(sys.exc_info()[0])
  finally:
    logging.info('\\ End GetStatistics /')

def create_database(host, user, password):
  try:
    db_connection = MySQLdb.connect(host, user, password)
    cursor = db_connection.cursor()
    cursor.execute('CREATE DATABASE IF NOT EXISTS `OpenStackStat` CHARACTER SET utf8')
    cursor.execute('USE OpenStackStat')
    cursor.execute('''CREATE TABLE IF NOT EXISTS `cpu_statistic` (
                     `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
                     `instance_name` VARCHAR(255) NOT NULL,
                     `vm_name` VARCHAR(255) NOT NULL,
                     `host_name` VARCHAR(255) NOT NULL,
                     `pid` INT UNSIGNED NOT NULL,
                     `CPU` REAL NOT NULL,
                     `stat_date` DATETIME NOT NULL,
                     PRIMARY KEY(`id`) 
                     ) ENGINE = InnoDB DEFAULT CHARSET = utf8
                  ''')
    db_connection.commit()
  except Exception as e:
    db_connection.rollback()
    logging.warning(e)
    raise e
  finally:
    cursor.close()
    db_connection.close()

if __name__ == '__main__':
  try:
    logging.info('START GET STATISTICS')
    config = ConfigParser.ConfigParser()
    config.read('/etc/ost-stat/ost-stat.conf')
    host = config.get('DataBase', 'host')
    user = config.get('DataBase', 'user')
    password = config.get('DataBase', 'password')
    db_name = config.get('DataBase', 'db_name')
    logging.info('DB configuration: host='+host+';user='+user+';password='+password+';db_name='+db_name)
    create_database(host, user, password)
    while(True):
      VM_map = GetStatistics()
      if(VM_map != None):
        print(VM_map)
        db = MySQLdb.connect(host, user, password, db_name)
        cursor = db.cursor()
        try:
          for instname in VM_map:
            sql = '''insert into cpu_statistic (instance_name, vm_name, host_name, pid, CPU, stat_date) '''
            sql += '''values (%s, %s, %s, %s, %s, %s)'''
            values = (instname, VM_map[instname]['VM_name'], VM_map[instname]['host'], VM_map[instname]['pid'], VM_map[instname]['CPU'], VM_map[instname]['stat_date'])
            cursor.execute(sql, values)
          db.commit()
        except Exception as e:
          db.rollback()
          logging.warning(e)
        finally:
          cursor.close()
          db.close()
      time.sleep(60)
  except Exception as e:
    logging.warning(e)
  finally:
    logging.info('STOP GET STATISTICS')